package com.danciyixue.boot.dcyx.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.danciyixue.boot.common.constant.BizRoleConstants;
import com.danciyixue.boot.core.security.util.SecurityUtils;
import com.danciyixue.boot.dcyx.converter.DcyxUsersConverter;
import com.danciyixue.boot.dcyx.mapper.DcyxUsersMapper;
import com.danciyixue.boot.dcyx.model.entity.DcyxUsers;
import com.danciyixue.boot.dcyx.model.enums.EffectiveDurationOption;
import com.danciyixue.boot.dcyx.model.form.DcyxUsersBatchCreateForm;
import com.danciyixue.boot.dcyx.model.form.DcyxUsersForm;
import com.danciyixue.boot.dcyx.model.query.DcyxUsersQuery;
import com.danciyixue.boot.dcyx.model.vo.DcyxAssignTeacherBatchResultVO;
import com.danciyixue.boot.dcyx.model.vo.DcyxUsersBatchCreateResultVO;
import com.danciyixue.boot.dcyx.model.vo.DcyxUsersVO;
import com.danciyixue.boot.dcyx.service.DcyxUsersService;
import com.danciyixue.boot.system.service.UserService;
import lombok.RequiredArgsConstructor;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.security.SecureRandom;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 用户信息服务实现类
 *
 * @author youlaitech
 * @since 2025-06-15 16:58
 */
@Service
@DS("dcyx")
@RequiredArgsConstructor
public class DcyxUsersServiceImpl extends ServiceImpl<DcyxUsersMapper, DcyxUsers> implements DcyxUsersService {

    private static final String USERNAME_PREFIX = "linshi";
    private static final String USERNAME_SEPARATOR = "_";
    private static final DateTimeFormatter USERNAME_TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
    private static final int USERNAME_RANDOM_TAG_LENGTH = 3;
    private static final int USERNAME_MIN_SEQUENCE_WIDTH = 6;
    private static final char[] USERNAME_RANDOM_POOL = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789".toCharArray();
    private static final SecureRandom SECURE_RANDOM = new SecureRandom();
    private static final String DEFAULT_INITIAL_PASSWORD = "123456";
    private static final int DEFAULT_STATUS_ENABLED = 1;

    private final DcyxUsersConverter dcyxUsersConverter;
    private final PasswordEncoder passwordEncoder;
    private final UserService userService;

    /**
    * 获取用户信息分页列表
    *
    * @param queryParams 查询参数
    * @return {@link IPage<DcyxUsersVO>} 用户信息分页列表
    */
    @Override
    public IPage<DcyxUsersVO> getDcyxUsersPage(DcyxUsersQuery queryParams) {
        Set<String> roleCodes = SecurityUtils.getRoles();
        boolean isTeacher = roleCodes.contains(BizRoleConstants.ROLE_TEACHER);
        boolean hasPrivilege = roleCodes.contains(BizRoleConstants.ROLE_ADMIN)
                || roleCodes.contains(BizRoleConstants.ROLE_PRESIDENT)
                || SecurityUtils.isRoot();

        if (isTeacher && !SecurityUtils.isRoot()) {
            queryParams.setTeacherId(SecurityUtils.getUserId());
        } else if (!hasPrivilege) {
            queryParams.setTeacherId(null);
        }

        Page<DcyxUsersVO> pageVO = this.baseMapper.getDcyxUsersPage(
                new Page<>(queryParams.getPageNum(), queryParams.getPageSize()),
                queryParams
        );
        return pageVO;
    }
    
    /**
     * 获取用户信息表单数据
     *
     * @param id 用户信息ID
     * @return 用户信息表单数据
     */
    @Override
    public DcyxUsersForm getDcyxUsersFormData(Long id) {
        DcyxUsers entity = this.getById(id);
        return dcyxUsersConverter.toForm(entity);
    }
    
    /**
     * 新增用户信息
     *
     * @param formData 用户信息表单对象
     * @return 是否新增成功
     */
    @Override
    public boolean saveDcyxUsers(DcyxUsersForm formData) {
        DcyxUsers entity = dcyxUsersConverter.toEntity(formData);
        return this.save(entity);
    }
    
    /**
     * 更新用户信息
     *
     * @param id   用户信息ID
     * @param formData 用户信息表单对象
     * @return 是否修改成功
     */
    @Override
    public boolean updateDcyxUsers(Long id,DcyxUsersForm formData) {
        DcyxUsers entity = dcyxUsersConverter.toEntity(formData);
        return this.updateById(entity);
    }
    
    /**
     * 删除用户信息
     *
     * @param ids 用户信息ID，多个以英文逗号(,)分割
     * @return 是否删除成功
     */
    @Override
    public boolean deleteDcyxUserss(String ids) {
        Assert.isTrue(StrUtil.isNotBlank(ids), "删除的用户信息数据为空");
        // 逻辑删除
        List<Long> idList = Arrays.stream(ids.split(","))
                .map(Long::parseLong)
                .toList();
        return this.removeByIds(idList);
    }

    /**
     * 根据用户名获取用户信息
     *
     * @param username 用户名
     * @return 用户信息
     */
    @Override
    public DcyxUsers getByUsername(String username) {
        return this.baseMapper.getByUsername(username);
    }

    @Override
    public DcyxUsersVO getDcyxUsersMe() {
        Long userId = SecurityUtils.getUserId();
        return this.baseMapper.getDcyxUserMe(userId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean assignTeacher(Long userId, Long teacherId) {
        Assert.notNull(userId, "学生ID不能为空");
        Assert.notNull(teacherId, "教师ID不能为空");

        DcyxUsers student = this.getById(userId);
        Assert.notNull(student, "学生不存在");

        userService.validateTeacher(teacherId);

        if (Objects.equals(teacherId, student.getAssignedTeacherId())) {
            return true;
        }

        return this.update(new LambdaUpdateWrapper<DcyxUsers>()
                .eq(DcyxUsers::getId, userId)
                .set(DcyxUsers::getAssignedTeacherId, teacherId)
        );
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public DcyxAssignTeacherBatchResultVO assignTeacherBatch(Long teacherId, List<Long> userIds) {
        Assert.notNull(teacherId, "教师ID不能为空");
        Assert.notEmpty(userIds, "学生ID列表不能为空");

        userService.validateTeacher(teacherId);

        List<Long> distinctUserIds = userIds.stream()
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());

        List<DcyxUsers> students = distinctUserIds.isEmpty() ? new ArrayList<>() : this.listByIds(distinctUserIds);
        Set<Long> existIds = students.stream().map(DcyxUsers::getId).collect(Collectors.toSet());

        List<Long> invalidIds = distinctUserIds.stream()
                .filter(id -> !existIds.contains(id))
                .collect(Collectors.toList());

        List<Long> skippedIds = new ArrayList<>();
        int updatedCount = 0;

        for (DcyxUsers student : students) {
            if (Objects.equals(teacherId, student.getAssignedTeacherId())) {
                skippedIds.add(student.getId());
                continue;
            }
            boolean updated = this.update(new LambdaUpdateWrapper<DcyxUsers>()
                    .eq(DcyxUsers::getId, student.getId())
                    .set(DcyxUsers::getAssignedTeacherId, teacherId)
            );
            if (updated) {
                updatedCount++;
            }
        }

        DcyxAssignTeacherBatchResultVO result = new DcyxAssignTeacherBatchResultVO();
        result.setTotal(userIds.size());
        result.setUpdatedCount(updatedCount);
        result.setSkippedCount(skippedIds.size());
        result.setSkippedIds(skippedIds);
        result.setInvalidUserIds(invalidIds);
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<DcyxUsersBatchCreateResultVO> batchCreateAccounts(DcyxUsersBatchCreateForm form) {
        Assert.notNull(form, "批量创建请求不能为空");
        Integer quantity = form.getQuantity();
        Assert.notNull(quantity, "批量创建数量不能为空");
        Assert.isTrue(quantity > 0, "批量创建数量必须大于0");

        EffectiveDurationOption durationOption = form.getEffectiveDuration();
        Assert.notNull(durationOption, "有效时长不能为空");

        LocalDateTime now = LocalDateTime.now();
        String usernamePrefix = buildUsernamePrefix(durationOption, now);
        int sequenceWidth = Math.max(USERNAME_MIN_SEQUENCE_WIDTH, String.valueOf(quantity).length());

        List<DcyxUsers> entities = new ArrayList<>(quantity);
        List<DcyxUsersBatchCreateResultVO> results = new ArrayList<>(quantity);

        for (int i = 0; i < quantity; i++) {
            String username = usernamePrefix + formatSequence(i + 1, sequenceWidth);
            DcyxUsers entity = new DcyxUsers();
            entity.setUsername(username);
            entity.setPassword(passwordEncoder.encode(DEFAULT_INITIAL_PASSWORD));
            entity.setEffectiveDuration(durationOption);
            entity.setStatus(DEFAULT_STATUS_ENABLED);
            entities.add(entity);

            DcyxUsersBatchCreateResultVO vo = new DcyxUsersBatchCreateResultVO();
            vo.setUsername(username);
            vo.setInitialPassword(DEFAULT_INITIAL_PASSWORD);
            vo.setEffectiveDuration(durationOption);
            results.add(vo);
        }

        boolean saved = this.saveBatch(entities);
        Assert.isTrue(saved, "批量创建账号失败");
        return results;
    }

    /**
     * 重置用户密码
     *
     * @param userId   用户ID
     * @param password 新密码
     * @return 是否重置成功
     */
    @Override
    public boolean resetPassword(Long userId, String password) {
        Assert.notNull(userId, "用户ID不能为空");
        Assert.isTrue(StrUtil.isNotBlank(password), "密码不能为空");
        return this.update(new LambdaUpdateWrapper<DcyxUsers>()
                .eq(DcyxUsers::getId, userId)
                .set(DcyxUsers::getPassword, passwordEncoder.encode(password))
        );
    }

    private String buildUsernamePrefix(EffectiveDurationOption option, LocalDateTime timestamp) {
        return USERNAME_PREFIX + option.getMonths() + "m" + USERNAME_SEPARATOR
                + USERNAME_TIMESTAMP_FORMATTER.format(timestamp) + USERNAME_SEPARATOR
                + randomTag(USERNAME_RANDOM_TAG_LENGTH) + USERNAME_SEPARATOR;
    }

    private String randomTag(int length) {
        char[] chars = new char[length];
        for (int i = 0; i < length; i++) {
            chars[i] = USERNAME_RANDOM_POOL[SECURE_RANDOM.nextInt(USERNAME_RANDOM_POOL.length)];
        }
        return new String(chars);
    }

    private String formatSequence(int number, int width) {
        return String.format("%0" + width + "d", number);
    }


}
